/**********************************************
*  Classes:   MeshNode
*  Desc:      Defines a mesh node. Derives 
*			  from GeometryNode. Uses Vertex Buffer
*			  Objects for rendering
*  Author:    Vaios Kalpias-Ilias (C) 2008-2009
***********************************************/

#include "MeshNode.h"


//----------------------------------------------
/**
* Ctor
*/
MeshNode::MeshNode()
: m_uiVBOVertices(0),
  m_uiVBONormals(0),
  m_uiVBOTexCoords(0),
  m_iNumVertices(0)
{
	

	// VBO Function Pointers
	glDeleteBuffersARB = 
			(PFNGLDELETEBUFFERSARBPROC) wglGetProcAddress("glDeleteBuffersARB");

	glBindBufferARB = 
			((PFNGLBINDBUFFERARBPROC) wglGetProcAddress("glBindBufferARB"));

	glGenBuffersARB = 
			((PFNGLGENBUFFERSARBPROC) wglGetProcAddress("glGenBuffersARB"));

	glBufferDataARB = 
			((PFNGLBUFFERDATAARBPROC) wglGetProcAddress("glBufferDataARB"));
}	
//----------------------------------------------
/**
* Dtor
*/
MeshNode::~MeshNode()
{
	unsigned int nBuffers[3] = 
	{ m_uiVBOVertices, m_uiVBOTexCoords,  m_uiVBONormals};
	glDeleteBuffersARB( 3, nBuffers );
}
//----------------------------------------------
/**
* Clones mesh
*/
GeometryNode* MeshNode::Clone()
{
	MeshNode* g = new MeshNode();
	if (!g) return 0;

	g->m_iNumVertices = m_iNumVertices;
	g->m_sName = m_sName;
	g->m_uiVBONormals = m_uiVBONormals;
	g->m_uiVBOTexCoords = m_uiVBOTexCoords;
	g->m_uiVBOVertices = m_uiVBOVertices;
	
	return g;
}
//----------------------------------------------
/**
* Renders mesh
*/
void MeshNode::RenderShape()
{
	
	if (m_uiVBOVertices)
	{
		// Enable Pointers
		// Enable Vertex Arrays
		glEnableClientState( GL_VERTEX_ARRAY );		
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_uiVBOVertices );
		// Set The Vertex Pointer To The Vertex Buffer
		glVertexPointer( 3, GL_FLOAT, 0, (char *) 0 );

		if (m_uiVBOTexCoords)
		{
			// Enable Texture Coord Arrays
			glEnableClientState( GL_TEXTURE_COORD_ARRAY );	
			glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_uiVBOTexCoords );
			// Set The TexCoord Pointer To The TexCoord Buffer
			glTexCoordPointer( 2, GL_FLOAT, 0, (char *) 0 );	
		}
		
		if (m_uiVBONormals)
		{
			// Enable Normal Arrays
			glEnableClientState( GL_NORMAL_ARRAY );
			glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_uiVBONormals );
			// Set The Vertex Pointer To The Normal Buffer
			glNormalPointer( GL_FLOAT, 0, (char *) 0 );
		}

		// Render
		// Draw All Of The Triangles At Once
		glDrawArrays( GL_TRIANGLES, 0, m_iNumVertices );	

		// Disable Pointers
		// Disable Vertex Arrays
		glDisableClientState( GL_VERTEX_ARRAY );

		if (m_uiVBOTexCoords)
		{
			// Disable Texture Coord Arrays
			glDisableClientState( GL_TEXTURE_COORD_ARRAY );				
		}
		
		if (m_uiVBONormals)
		{
			// Disable Normal Arrays
			glDisableClientState( GL_NORMAL_ARRAY );	
		}
		
	}
}
//----------------------------------------------
/**
* Creates VBO's 
*/
bool MeshNode::BindToMesh(MathLib::Vector3* vertices,
					MathLib::Vector3* normals,
					MathLib::Vector2* texCoords,
					int numOfVertices)
{
	
	if (!vertices)
	{
		if (normals)
			delete [] normals;

		if (texCoords)
			delete [] texCoords;
		return false;
	}

	m_iNumVertices = numOfVertices;
	
	// Get A Valid Name
	glGenBuffersARB( 1, &m_uiVBOVertices );		
	// Bind The Buffer
	glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_uiVBOVertices );			
	// Load The Data
	glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
		numOfVertices*3*sizeof(float), vertices, GL_STATIC_DRAW_ARB );

	delete [] vertices; vertices = 0;
	
	if (normals)
	{
		// Get A Valid Name
		glGenBuffersARB( 1, &m_uiVBONormals );		
		// Bind The Buffer
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_uiVBONormals );			
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
			numOfVertices*3*sizeof(float), normals, GL_STATIC_DRAW_ARB );

		delete [] normals; normals = 0;
	}

	if (texCoords)
	{
		// Generate And Bind The Texture Coordinate Buffer
		// Get A Valid Name
		glGenBuffersARB( 1, &m_uiVBOTexCoords );							
		// Bind The Buffer
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_uiVBOTexCoords );		
		// Load The Data
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
			numOfVertices*2*sizeof(float), texCoords, GL_STATIC_DRAW_ARB );

		delete [] texCoords; texCoords = 0;
	}

	return true;
}
//----------------------------------------------
/**
* Opengl.Org code: checks if VBO's are supported
*/
bool MeshNode::IsVBOExtensionSupported()
{
	const unsigned char *pszExtensions = NULL;
	const unsigned char *pszStart;
	char* szTargetExtension = "GL_ARB_vertex_buffer_object";
	unsigned char *pszWhere, *pszTerminator;

	// Extension names should not have spaces
	pszWhere = (unsigned char *) strchr( szTargetExtension, ' ' );
	if( pszWhere || *szTargetExtension == '\0' )
		return false;

	// Get Extensions String
	pszExtensions = glGetString( GL_EXTENSIONS );

	// Search The Extensions String For An Exact Copy
	pszStart = pszExtensions;
	for(;;)
	{
		pszWhere = (unsigned char *) strstr( (const char *) pszStart, szTargetExtension );
		if( !pszWhere )
			break;
		pszTerminator = pszWhere + strlen( szTargetExtension );
		if( pszWhere == pszStart || *( pszWhere - 1 ) == ' ' )
			if( *pszTerminator == ' ' || *pszTerminator == '\0' )
				return true;
		pszStart = pszTerminator;
	}
	return false;
}


